#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright 2012 Daniel Baktiar (dbaktiar [at] gmail.com)
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

import glob
import os
import re
import tarfile
import logging
import shutil
import zipfile
import ConfigParser


# Constants
MAGIC_APPEND_STRING = '.$$SWPT$$'

class PackageTools(object):
    """
    This class encapsulate tools for packaging, and installation tasks.
    """

    def __init__(self, name, version, vendor):
        """
        The object initialiser, initialise with the name, version
        of the product package, and the vendor name.
        """
        self.package_name = name
        self.package_version = version
        self.package_vendor = vendor
        # initialise the logging components
        self.logger = logging.getLogger('swpt.PackageTools')
        self.logger.info('Logger initialised.')

    def display_info(self):
        """
        This method displays the information about the package.
        """
        print('\nProduct: %s\nVersion: %s\nVendor: %s\n\n' % \
                (self.package_name, self.package_version, self.package_vendor))
        self.logger.info('\n    Product: %s\n    Version: %s\n    Vendor: %s\n\n' % \
                (self.package_name, self.package_version, self.package_vendor))

    def prompt_yesno(self, prompt, default='Y'):
        """
        This method prompts the user for Yes/No answer.

        When default is set to empty string ('') the system will keep prompting 
        until Y/N answer is given correctly.
        When default is set to 'Y' or 'y' it will assume Yes answer
        (returning True) when the user just press ENTER (choose default).
        When default is set to 'N' or 'n' it will assume No answer
        (returning False) when the user just press ENTER (choose default).
        Otherwise prompt will again be displayed and waiting for 
        the answer.

        """
        if default == '':
            the_prompt = prompt + ' '
        elif default == 'Y' or default == 'y':
            the_prompt = prompt + ' (Y/n) '
        elif default == 'N' or default == 'n':
            the_prompt = prompt + ' (y/N) '

        while True:
            result = raw_input(the_prompt)
            if result == '':
                # set to default answer
                result = default
            if result == 'Y' or result == 'y':
                return True
            elif result == 'N' or result == 'n':
                return False

    def prompt_with_default(self, prompt, default):
        """
        This method prompts the user for input string.
        """
        the_prompt = prompt % default
        result = raw_input(the_prompt)
        print(result)
        if result == None or result == '':
            # set to default answer
            result = default
        return result

    def prompt_regex(self, prompt, regex, default=''):
        """
        This method prompts the user for a string, and then check whether the 
        answer match the given regex.
        """
        pass

    def is_int(self, num):
        """
        This method prompts the user for a string, and then check whether the 
        answer match the given regex.
        """
        try:
            int(num)
            return True
        except ValueError:
            return False

    def prompt_port(self):
        """
        This method prompts the application server ports to be used.
        """
        while True:
            result = raw_input('Port number? ')
            if self.is_int(result):
                return result
            else:
                print('Please enter an integer value for port number.')
        return result

    def create_tgz(self, tarball_name, base_folder, src_folder):
        """
        This methods will create the tarball to be stored
        at certain location (src_folder).
        """
        cwd = os.getcwd()
        print('cwd: [%s]' % cwd)
        os.chdir(base_folder)
        print('tarball_name: [%s]' % tarball_name)
        tar = tarfile.open(cwd + '/' + tarball_name, 'w:gz')
        os.chdir(base_folder)
        print('src_folder: [%s]' % src_folder)
        tar.add(src_folder)
        tar.close()

    def extract_tgz(self, tarball_name, dst_folder):
        """
        This method extracts tarball to certain location (dst_folder).
        """
        cwd = os.getcwd()
        tarball_fullpath = cwd + '/' + tarball_name
        print('Opening tarball: %s' % tarball_fullpath)
        tar = tarfile.open(tarball_fullpath)
        os.chdir(dst_folder)
        tar.extractall()
        tar.close()
        os.chdir(cwd)


class ConfigTools(object):
    """
    """
    def __init__(self):
        pass

    def exit_on_root(self, retval=1, 
                message='Cowardly refusing to run as root user.'):
        """
        This method refuse to run and raise SystemExit when the
        script is run on root user.
        """
        if os.getuid() == 0:
            print(message)
            raise SystemExit(retval)
        
    def replace_var_in_file(self, filename, var_name, new_value):
        """
        """
        tmp_filename = filename + MAGIC_APPEND_STRING
        os.rename(filename, tmp_filename)
        fin = file(tmp_filename)
        lines = []
        for line in fin:
            if var_name in line:
                line = line.replace(var_name, new_value)
            lines.append(line)
        fin.close()
        fout = file(filename, 'w')
        fout.writelines(lines)
        fout.close()
        shutil.copymode(tmp_filename, filename)
        os.remove(tmp_filename)
        

class JarTools(object):
    """
    """
    def __init__(self, filename):
        """
        """
        self.ct = ConfigTools()
        self.jarfile = zipfile.ZipFile(filename, 'a')
        self.inprocess = '$$$INPROCESS$$$.swpt'
        self.outer_inprocess = '$$$OUTER$INPROCESS$$$.swpt'

    def replace_var_in_archive(self, war_arcname, var_name, var_value):
        out_file = open(self.inprocess, 'w')
        """
        """
        bytes_stream = self.jarfile.read(war_arcname)
        out_file.write(bytes_stream)
        out_file.close()
        self.ct.replace_var_in_file(self.inprocess, var_name, var_value)
        self.jarfile.write(self.inprocess, war_arcname)
        os.remove(self.inprocess)

    def replace_var_in_war_lib(self, jar_filename, jar_arcname, var_name, var_value):
        """
        """
        war_arcname = 'WEB-INF/lib/' + jar_filename
        lib_jar_file = open(self.outer_inprocess, 'w')
        bytes_stream = self.jarfile.read(war_arcname)
        lib_jar_file.write(bytes_stream)
        lib_jar_file.close()
        # now we have the lib jar, named self.outer_inprocess
        self.recursive_jt = JarTools(self.outer_inprocess)
        self.recursive_jt.replace_var_in_archive(jar_arcname, var_name, var_value)
        self.recursive_jt.close()
        self.jarfile.write(self.outer_inprocess, war_arcname)
        os.remove(self.outer_inprocess)

    def close(self):
        """
        """
        self.jarfile.close()
        

class FolderTools(object):
    """
    This class encapsulate folder tools.
    """
    def __init__(self):
        pass

    def folder_exists(self, folder_name):
        self.cwd = os.getcwd()
        result = True
        try:
            os.chdir(folder_name)
        except OSError:
            result = False
        return result

    def assert_folder_exists(self, var_name, folder_name):
        """
        This method expects that the folder named 'folder_name'
        exists in the file system, otherwise it will display error
        message telling that the folder name variable 'var_name'
        does not exist, and then quit.
        """
        if self.folder_exists(folder_name):
            print('Folder %s (%s) found.' % (var_name, folder_name))
        else:
            print('ERROR. Folder %s (%s) does not exists.' % (var_name, folder_name))
            raise SystemExit
    


if __name__=='__main__':
    test = PackageTools()
    test.display_info()



DEFAULT_CONFIG_FILE = 'universal/template/taf-config.cfg'
CONFIG_TREE = [
    ('TAF Config', [
        'taf.config.version',
    ]),
    ('Client Particulars', [
        'company.name', 
        'company.shortname',
    ]),
    ('Packaging', [
        'packaging.code',
    ]),
    ('Database', [
        'database.product.vendor',
        'database.product.family',
        'database.product.version',
        'database.product.edition',
        'database.product.officialname',
    ]),
    ('Oracle Database', [
        'oracle.rac_deployment',
        'oracle.jdbc.type',
    ]),
]

class TafInstallerConfig(object):
    """
    TAF Installer Config module.
    Triquesta Application Framework(tm).

    Author: Daniel Baktiar, 2013-01-29.
  
    This class is a reader of universal configuration file which can be read
    from both Python installer script and Java project/code.
    The goal is to a minimal number of configuration file in one place.
    """

    def __init__(self, config_file=DEFAULT_CONFIG_FILE):
        self._cfg = ConfigParser.SafeConfigParser()
        self._cfg.read(config_file)

    def info(self):
        """
        This method shows the info.
        """

        for current_section, proplist in CONFIG_TREE:
            print('Section: [%s]' % current_section)
            for prop in proplist:
                propval = self._cfg.get(current_section, prop)
                print('    %s: [%s]' % (prop, self._cfg.get(current_section, prop)))

    def dummy(self):
        config = ConfigParser.ConfigParser()
        config.add_section('Section 1')
        config.add_section('Section 2')
        config.set('Section 1', 'a', 'b')
        config.set('Section 2', 'c', 'd')
        config.set('Section 1', 'e', '5')
        config.set('Section 2', 'f', '6')
        with open('example.cfg', 'wb') as configfile:
            config.write(configfile)
            configfile.close()

class FolderConfig(object):
    """
    There are 2 mainstream configuration:
      1) using a base folder, such as ones in RI, Olam, WBC.
         e.g. /appl/triquesta/tcm as the base
      2) configure folders separately.
         e.g. log files go to /var/log/triquesta/tcm,
              data files go to /var/data/triquesta/tcm,
              installation goes to /appl/triquesta/tcm.
         This approach si taken by UC, and ING. 
         However, UC package their own Tomcat app server.
    """
    def __init__(self, use_base_folder=True):
        self._use_base_folder = use_base_folder

    def set_default(self):
        """
        This method sets the default folder locations. This default is following an already existing
        installer which is already approved by many clients. When clients deviates form the default
        due to production support requirements, a customized folder location needs to be established.
        """
        self._config_map = dict()
        self._config_map['app.base'] = '/appl/triquesta/tcm'
        self._config_map['prog.base'] = self._config_map['app.base'] + '/prog'
        self._config_map['appserver.base'] = self._config_map['prog.base'] + '/apsv'
        self._config_map['app.data.base']  = self._config_map['prog.base'] + '/data'
        self._config_map['app.logs.base']  = self._config_map['prog.base'] + '/logs'
        self._config_map['app.logs.apsv.base']      = self._config_map['app.logs.base'] + '/apsv'
        self._config_map['app.logs.security.base']  = self._config_map['app.logs.base'] + '/security'
        self._config_map['app.logs.tcm.base']       = self._config_map['app.logs.base'] + '/tcm'
        self._config_map['app.logs.tesl.base']      = self._config_map['app.logs.base'] + '/tesl'

    def override(self, override_map):
        pass

def copy_all(src, dst):
    for f in glob.glob(src):
        shutil.copy(f, dst)

def scan_config_files(config_folder='/opt/sw/tri/tcm/conf'):
    listing = os.listdir(config_folder)
    files = dict()
    for infile in listing:
        if infile.endswith('.tafkey'):
            filepath = infile[:len(infile) - 7]
            if filepath in files:
                files[filepath] += 1
            else:
                files[filepath] = 1
        elif infile.endswith('.tafcrd'):
            filepath = infile[:len(infile) - 7]
            if filepath in files:
                files[filepath] += 2
            else:
                files[filepath] = 2
        else:
            print ''
    config_files = list()
    for f in files:
        if files[f] == 3:
            config_files.append(f)
    result = list()
    config_files.sort()
    line_num = 0
    for cf in config_files:
        line_num += 1
        result.append((line_num, cf,))
    return result;



if __name__ == '__main__':
    tic = TafInstallerConfig();
    tic.info()

